
#ifndef __SINK_HPP

#define __SINK_HPP

#include "util.hpp"
#include <memory>
#include <unistd.h>
#include <fstream>
#include <cassert>
#include <utility>
#include <sstream>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <jsoncpp/json/json.h>
#include <signal.h>
#include <time.h>

namespace Log
{
    class LogSink // 落地基类
    {
    public:
        using ptr = std::shared_ptr<LogSink>;
        virtual ~LogSink(){};
        virtual void log(const char *date, size_t len) = 0;
    };

    class StdoutSink : public LogSink // 标准输出落地
    {
    public:
        StdoutSink()
        {
        }
        virtual void log(const char *date, size_t len)
        {
            std::cout.write(date, len);
        }
    };

    class FileSink : public LogSink // 指定文件落地
    {
    private:
        std::string _pathname;
        std::ofstream _ofs;

    public:
        // 传入文件路径初始化化,同时打开文件,将句柄管理起来
        FileSink(const std::string &pathname) : _pathname(pathname)
        {
            // 防止文件路径不存在,先创建文件路径
            util::file::creatDirectory(util::file::path(_pathname));
            // 文件不存在打开时会自动创建,并以追加方式打开
            _ofs.open(_pathname, std::ios::binary | std::ios::app);
            // 判断是否打开成功
            assert(_ofs.is_open());
        }
        void log(const char *date, size_t len)
        {
            assert(_ofs.good()); // 判断是否处于就绪状态
            _ofs.write(date, len);
        }
    };

    class RollBySizeSink : public LogSink
    {
    private:
        std::string _basename; // 文件前缀名字
        std::ofstream _ofs;
        size_t _max_fsize; // 最大文件容量
        size_t _cur_fsize; // 当前文件容量
        size_t _count;     // 防止日志生成过快,在一秒内生成多个文件导致文件名重复,加一个计数
    private:
        std::string creatNewFile()
        {
            time_t t = util::date::getTime();
            struct tm lt;
            localtime_r(&t, &lt);
            std::stringstream filename;
            filename << _basename;
            filename << lt.tm_year + 1900; // 时间戳从1900年开始要加1900
            filename << lt.tm_mon + 1;     // 月份从0开始要加1
            filename << lt.tm_mday;
            filename << lt.tm_hour;
            filename << lt.tm_min;
            filename << lt.tm_sec;
            filename << _count++;
            filename << ".log";
            return filename.str();
        }

    public:
        // 构造需要传文件前缀名,文件允许最大值
        RollBySizeSink(const std::string &basename, size_t max_size) : _basename(basename), _max_fsize(max_size), _cur_fsize(0), _count(0)
        {
            std::string pathname = creatNewFile();
            // 防止文件路径不存在,先创建文件路径
            util::file::creatDirectory(util::file::path(pathname));
            // 文件不存在打开时会自动创建,并以追加方式打开
            _ofs.open(pathname, std::ios::binary | std::ios::app);
            // 判断是否打开成功
            assert(_ofs.is_open());
        }
        void log(const char *date, size_t len)
        {
            if (_cur_fsize >= _max_fsize)
            {
                std::string pathname = creatNewFile();
                _ofs.close(); // 先关闭之前的
                _ofs.open(pathname, std::ios::binary | std::ios_base::app);
                // 判断是否打开成功
                assert(_ofs.is_open());
                _cur_fsize = 0;
            }
            assert(_ofs.good()); // 判断是否处于就绪状态
            _cur_fsize += len;
            _ofs.write(date, len);
        }
    };

    //
    class CloudSink : public LogSink
    {
    private:
        int sock;
        struct sockaddr_in servaddr;
        time_t time_wait;
        size_t arq; // 重传次数

    public:
        CloudSink(std::string server_ip,int server_port,time_t time_wait,size_t arq):
        arq(arq),
        time_wait(time_wait)
        {
            // 获取服务端信息
            // std::ifstream ifs;
            // ifs.open("../logs/cloud.conf");
            // assert(ifs.is_open());
            // ifs.seekg(0, ifs.end);
            // int len = ifs.tellg();
            // ifs.seekg(0, ifs.beg);
            // char buf[len];
            // ifs.read(buf,len);
            // Json::Value root;
            // JsonUtil::UnSerialize(root, buf, len);
            // //获取配置文件信息
            // std::string server_ip = root["server_ip"].asString();
            // int port = root["server_port"].asInt();
            // time_wait = root["time_wait"].asUInt();
            // arq = root["ARQ"].asUInt();

            servaddr.sin_family = AF_INET;
            servaddr.sin_port = htons(server_port);
            servaddr.sin_addr.s_addr = inet_addr(server_ip.c_str());
            // 获取套接字
            sock = socket(AF_INET, SOCK_DGRAM, 0); // ipv4,udp
        }
        void log(const char *date, size_t len)
        {
            _log(date, len, arq);
        }

    private:
        void _log(const char *date, size_t len, size_t cnt)
        {
            if (cnt == 0)
                return; // 如果超过重传次数则会直接返回
            // 发送后等待两个MSL,如果服务端还没应答则从新发送
            sendto(sock, date, len, MSG_CONFIRM, (const struct sockaddr *)&servaddr, sizeof(servaddr));
            // 采用select实现重传
            fd_set fds;
            struct timeval timeout;
            FD_ZERO(&fds);
            FD_SET(sock, &fds);
            timeout.tv_sec = time_wait;
            timeout.tv_usec = 0;

            int ret = select(sock + 1, &fds, nullptr, nullptr, &timeout);
            if (ret == -1)
            {
                std::cout << "select fail\n";
            }
            else if (ret == 0) // 超时重传
            {
                std::cout << "time out "<<cnt<<'\n';
                _log(date, len, --cnt);
            }
            else
            {
                char buffer[16];
                read(sock,buffer,16);
            }
        }
    };

    class SinkFactory
    {
    public:
        template <typename SinkType, typename... Args>
        static LogSink::ptr create(Args &&...args)
        {
            return std::make_shared<SinkType>(std::forward<Args>(args)...); // 完美转发传递
        }
    };
} // namespace Log

#endif // !__SINK_HPP
